package com.xinxin.blockchain.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.xinxin.base.exception.BusinessException;
import com.xinxin.blockchain.asset.enums.AssetBusinessTypeEnum;
import com.xinxin.blockchain.asset.enums.AssetTypeEnum;
import com.xinxin.blockchain.asset.template.*;
import com.xinxin.blockchain.constant.ErrorCode;
import com.xinxin.blockchain.dao.impl.AccountAssetDao;
import com.xinxin.blockchain.model.AccountAsset;
import com.xinxin.blockchain.service.IAccountAssetService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;

/**
 * @author ys
 * @date 2021/5/24
 */
@Slf4j
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class AccountAssetServiceImpl extends AccountAssetDao implements IAccountAssetService {

    @Autowired
    private IncreaseAssetTemplate increaseAssetTemplate;
    @Autowired
    private CostAssetTemplate costAssetTemplate;
    @Autowired
    private FrozenAssetTemplate frozenAssetTemplate;
    @Autowired
    private UnFrozenAssetTemplate unFrozenAssetTemplate;
    @Autowired
    private CostFrozenAssetTemplate costFrozenAssetTemplate;

    @Override
    public AccountAsset getByType(Long accountId, Integer type) {
        List<AccountAsset> assetList = super.listByType(accountId, type);
        if (assetList.size() > 1) {
            log.warn("duplicate asset: {}", assetList.get(0).getId());
            throw new BusinessException(ErrorCode.ASSET_DUPLICATE);
        }

        return assetList.isEmpty() ? null : assetList.get(0);
    }

    @Override
    public AccountAsset forceGetByType(Long accountId, Integer type) {
        AccountAsset asset = getByType(accountId, type);
        if (asset == null) {
            throw new BusinessException(ErrorCode.ASSET_NOT_FOUND);
        }

        return asset;
    }

    @Override
    @Transactional
    public boolean optimisticLockUpdate(AccountAsset asset) {
        Wrapper<AccountAsset> updateWrapper = new UpdateWrapper<AccountAsset>().lambda()
                .set(AccountAsset::getVersion, asset.getVersion() + 1)
                .set(asset.getAvailable() != null, AccountAsset::getAvailable, asset.getAvailable())
                .set(asset.getFrozen() != null, AccountAsset::getFrozen, asset.getFrozen())
                .set(asset.getTotal() != null, AccountAsset::getTotal, asset.getTotal())
                .eq(AccountAsset::getDataStatus, true)
                .eq(AccountAsset::getId, asset.getId())
                .eq(AccountAsset::getVersion, asset.getVersion());
        return super.update(updateWrapper);
    }

    @Override
    @Transactional
    public AccountAsset createAsset(Long accountId, Integer type) {
        AssetTypeEnum assetType = AssetTypeEnum.getByType(type);

        AccountAsset asset = new AccountAsset()
                .setAccountId(accountId)
                .setType(type)
                .setTypeText(assetType.getDesc());
        super.save(asset);

        log.info("创建账户资产，资产id：{}，账户id：{}，资产名称：{}", asset.getId(), accountId, assetType.getDesc());

        return super.getById(asset.getId());
    }

    @Override
    @Transactional
    public AccountAsset getOrCreate(Long accountId, Integer type) {
        AccountAsset asset = getByType(accountId, type);
        if (asset == null) {
            asset = createAsset(accountId, type);
        }
        return asset;
    }

    @Override
    @Transactional
    public AccountAsset increase(Long accountId, Integer assetType, BigDecimal amount, AssetBusinessTypeEnum businessType) {
        return increaseAssetTemplate.operate(getOrCreate(accountId, assetType), amount, businessType);
    }

    @Override
    @Transactional
    public AccountAsset cost(Long accountId, Integer assetType, BigDecimal amount, AssetBusinessTypeEnum businessType) {
        return costAssetTemplate.operate(forceGetByType(accountId, assetType), amount, businessType);
    }

    @Override
    @Transactional
    public AccountAsset frozen(Long accountId, Integer assetType, BigDecimal amount, AssetBusinessTypeEnum businessType) {
        return frozenAssetTemplate.operate(forceGetByType(accountId, assetType), amount, businessType);
    }

    @Override
    @Transactional
    public AccountAsset unFrozen(Long accountId, Integer assetType, BigDecimal amount, AssetBusinessTypeEnum businessType) {
        return unFrozenAssetTemplate.operate(forceGetByType(accountId, assetType), amount, businessType);
    }

    @Override
    @Transactional
    public AccountAsset costFrozen(Long accountId, Integer assetType, BigDecimal amount, AssetBusinessTypeEnum businessType) {
        return costFrozenAssetTemplate.operate(forceGetByType(accountId, assetType), amount, businessType);
    }
}
